//------------------------------------------------------------------------------
// <copyright company="Telligent Systems">
//     Copyright (c) Telligent Systems Corporation.  All rights reserved.
// </copyright> 
//------------------------------------------------------------------------------

using System.Collections;
using System.Text;
using System.Web.Caching;

namespace CommunityServer.Components
{
	public class Tags
	{
		#region GetTags

		public static ArrayList GetTags()
		{
			return GetTags(true);
		}

		public static ArrayList GetTags(bool cacheable)
		{
			CSContext context = CSContext.Current;
			string cacheKey = string.Format("Tags-S:{0}-R:{1}", context.SettingsID, context.User.RoleKey);

			ArrayList tags = cacheable ? (ArrayList) CSCache.Get(cacheKey) : null;
			if (tags == null)
			{
				tags = CommonDataProvider.Instance().GetTags();

				if (cacheable)
					CSCache.Insert(cacheKey, tags, CSCache.MinuteFactor * 10, CacheItemPriority.Normal);
			}

			return tags;
		}

		public static ArrayList GetTagsBySection(int sectionID)
		{
			return GetTagsBySections(new int[] { sectionID });
		}

		public static ArrayList GetTagsBySections(int[] sectionIDs)
		{
			return GetTagsBySections(sectionIDs, true);
		}

		public static ArrayList GetTagsBySections(int[] sectionIDs, bool cacheable)
		{
			CSContext context = CSContext.Current;
			string cacheKey = string.Format("Tags-S:{0}-R:{1}-S:{2}", context.SettingsID, context.User.RoleKey, MakeKeyString(sectionIDs));

			ArrayList tags = cacheable ? (ArrayList) CSCache.Get(cacheKey) : null;
			if (tags == null)
			{
				tags = CommonDataProvider.Instance().GetTagsBySections(sectionIDs);

				if (cacheable)
					CSCache.Insert(cacheKey, tags, CSCache.MinuteFactor * 3, CacheItemPriority.Normal);
			}

			return tags;
		}

		public static ArrayList GetTagsByGroup(int groupID)
		{
			return GetTagsByGroups(new int[] { groupID });
		}

		public static ArrayList GetTagsByGroups(int[] groupIDs)
		{
			return GetTagsByGroups(groupIDs, true);
		}

		public static ArrayList GetTagsByGroups(int[] groupIDs, bool cacheable)
		{
			CSContext context = CSContext.Current;
			string cacheKey = string.Format("Tags-S:{0}-R:{1}-G:{2}", context.SettingsID, context.User.RoleKey, MakeKeyString(groupIDs));

			ArrayList tags = cacheable ? (ArrayList) CSCache.Get(cacheKey) : null;
			if (tags == null)
			{
				tags = CommonDataProvider.Instance().GetTagsByGroups(groupIDs);

				if (cacheable)
					CSCache.Insert(cacheKey, tags, CSCache.MinuteFactor * 3, CacheItemPriority.Normal);
			}

			return tags;
		}

		public static ArrayList GetTags(ApplicationType applicationType)
		{
			return GetTags(applicationType, true);
		}

		public static ArrayList GetTags(ApplicationType applicationType, bool cacheable)
		{
			CSContext context = CSContext.Current;
			string cacheKey = string.Format("Tags-S:{0}-R:{1}-A:{2}", context.SettingsID, context.User.RoleKey, applicationType);

			ArrayList tags = cacheable ? (ArrayList) CSCache.Get(cacheKey) : null;
			if (tags == null)
			{
				tags = CommonDataProvider.Instance().GetTags(applicationType);

				if (cacheable)
					CSCache.Insert(cacheKey, tags, CSCache.MinuteFactor * 10, CacheItemPriority.Normal);
			}

			return tags;
		}

		public static ArrayList GetTags(string[] tags)
		{
			return GetTags(tags, true);
		}

		public static ArrayList GetTags(string[] tags, bool cacheable)
		{
			CSContext context = CSContext.Current;
			string cacheKey = string.Format("Tags-S:{0}-R:{1}-T:{2}", context.SettingsID, context.User.RoleKey, GetTagKeyComponent(tags));

			ArrayList newTags = cacheable ? (ArrayList) CSCache.Get(cacheKey) : null;
			if (newTags == null)
			{
				newTags = CommonDataProvider.Instance().GetTags(tags);

				if (cacheable)
				{
					if (tags == null || tags.Length < 2)
						CSCache.Insert(cacheKey, newTags, CSCache.MinuteFactor * 10, CacheItemPriority.Normal);
					else
						CSCache.Insert(cacheKey, newTags, CSCache.SecondFactorCalculate(30), CacheItemPriority.Low);
				}
			}

			return newTags;
		}

		public static ArrayList GetTagsBySection(int sectionID, string[] tags)
		{
			return GetTagsBySections(new int[] { sectionID }, tags);
		}

		public static ArrayList GetTagsBySections(int[] sectionIDs, string[] tags)
		{
			return GetTagsBySections(sectionIDs, tags, true);
		}

		public static ArrayList GetTagsBySections(int[] sectionIDs, string[] tags, bool cacheable)
		{
			CSContext context = CSContext.Current;
			string cacheKey = string.Format("Tags-S:{0}-R:{1}-S:{2}-T:{3}", context.SettingsID, context.User.RoleKey, MakeKeyString(sectionIDs), GetTagKeyComponent(tags));

			ArrayList newTags = cacheable ? (ArrayList) CSCache.Get(cacheKey) : null;
			if (newTags == null)
			{
				newTags = CommonDataProvider.Instance().GetTagsBySections(sectionIDs, tags);

				if (cacheable)
				{
					if (tags == null || tags.Length < 2)
						CSCache.Insert(cacheKey, newTags, CSCache.MinuteFactor * 3, CacheItemPriority.Normal);
					else
						CSCache.Insert(cacheKey, newTags, CSCache.SecondFactorCalculate(10), CacheItemPriority.Low);
				}
			}

			return newTags;
		}

		public static ArrayList GetTagsByGroup(int groupID, string[] tags)
		{
			return GetTagsByGroups(new int[] { groupID }, tags);
		}

		public static ArrayList GetTagsByGroups(int[] groupIDs, string[] tags)
		{
			return GetTagsByGroups(groupIDs, tags, true);
		}

		public static ArrayList GetTagsByGroups(int[] groupIDs, string[] tags, bool cacheable)
		{
			CSContext context = CSContext.Current;
			string cacheKey = string.Format("Tags-S:{0}-R:{1}-G:{2}-T:{3}", context.SettingsID, context.User.RoleKey, MakeKeyString(groupIDs), GetTagKeyComponent(tags));

			ArrayList newTags = cacheable ? (ArrayList) CSCache.Get(cacheKey) : null;
			if (tags == null)
			{
				newTags = CommonDataProvider.Instance().GetTagsByGroups(groupIDs, tags);

				if (cacheable)
				{
					if (tags == null || tags.Length < 2)
						CSCache.Insert(cacheKey, newTags, CSCache.MinuteFactor * 3, CacheItemPriority.Normal);
					else
						CSCache.Insert(cacheKey, newTags, CSCache.SecondFactorCalculate(30), CacheItemPriority.Low);
				}
			}

			return newTags;
		}

		public static ArrayList GetTags(ApplicationType applicationType, string[] tags)
		{
			return GetTags(applicationType, tags, true);
		}

		public static ArrayList GetTags(ApplicationType applicationType, string[] tags, bool cacheable)
		{
			CSContext context = CSContext.Current;
			string cacheKey = string.Format("Tags-S:{0}-R:{1}-A:{2}-T:{3}", context.SettingsID, context.User.RoleKey, applicationType, GetTagKeyComponent(tags));

			ArrayList newTags = cacheable ? (ArrayList) CSCache.Get(cacheKey) : null;
			if (newTags == null)
			{
				newTags = CommonDataProvider.Instance().GetTags(applicationType, tags);

				if (cacheable)
				{
					if (tags == null || tags.Length < 2)
						CSCache.Insert(cacheKey, newTags, CSCache.MinuteFactor * 10, CacheItemPriority.Normal);
					else
						CSCache.Insert(cacheKey, newTags, CSCache.SecondFactorCalculate(30), CacheItemPriority.Low);
				}
			}

			return newTags;
		}

		private static string MakeKeyString(int[] ids)
		{
			StringBuilder sb = new StringBuilder();
			foreach (int i in ids)
			{
				if (sb.Length > 0)
					sb.Append(",");

				sb.Append(i.ToString());
			}
			
			return sb.ToString();
		}

		#endregion

		#region GetPostsMatchingTags

		public static SearchResultSet GetPostsMatchingTags(string[] tags, int pageIndex, int pageSize)
		{
			return GetPostsMatchingTags(tags, pageIndex, pageSize, true, true);	
		}

		public static SearchResultSet GetPostsMatchingTags(int sectionID, string[] tags, int pageIndex, int pageSize)
		{
			return GetPostsMatchingTags(sectionID, tags, pageIndex, pageSize, true, true);	
		}

		public static SearchResultSet GetPostsMatchingTags(ApplicationType applicationType, string[] tags, int pageIndex, int pageSize)
		{
			return GetPostsMatchingTags(applicationType, tags, pageIndex, pageSize, true, true);
		}

		public static SearchResultSet GetPostsMatchingTags(string[] tags, int pageIndex, int pageSize, bool andTags)
		{
			return GetPostsMatchingTags(tags, pageIndex, pageSize, andTags, true);	
		}

		public static SearchResultSet GetPostsMatchingTags(int sectionID, string[] tags, int pageIndex, int pageSize, bool andTags)
		{
			return GetPostsMatchingTags(sectionID, tags, pageIndex, pageSize, andTags, true);	
		}

		public static SearchResultSet GetPostsMatchingTags(ApplicationType applicationType, string[] tags, int pageIndex, int pageSize, bool andTags)
		{
			return GetPostsMatchingTags(applicationType, tags, pageIndex, pageSize, andTags, true);
		}

		public static SearchResultSet GetPostsMatchingTags(string[] tags, int pageIndex, int pageSize, bool andTags, bool cacheable)
		{
			CSContext context = CSContext.Current;
			string cacheKey = string.Format("TagPosts-S:{0}-R:{1}-T:{2}-PI:{3}-PS:{4}-A:{5}", context.SettingsID, context.User.RoleKey, GetTagKeyComponent(tags), pageIndex, pageSize, andTags);

			SearchResultSet result = cacheable ? (SearchResultSet) CSCache.Get(cacheKey) : null;
			if (result == null)
			{
				SearchQuery query = new SearchQuery();
				query.PageSize = pageSize;
				query.PageIndex = pageIndex;
				query.SortBy = SearchSort.DateDescending;
				query.Tags = tags;
				query.LogicallyOrTags = !andTags;

				result = CSSearch.Search(query);

				if (cacheable)
				{
					if (pageIndex == 0 && (tags == null || tags.Length < 2))
						CSCache.Insert(cacheKey, result, CSCache.MinuteFactor * 10, CacheItemPriority.Normal);
					else
						CSCache.Insert(cacheKey, result, CSCache.SecondFactorCalculate(30), CacheItemPriority.Low);
				}
			}

			return result;
		}

		public static SearchResultSet GetPostsMatchingTags(int sectionID, string[] tags, int pageIndex, int pageSize, bool andTags, bool cacheable)
		{
			CSContext context = CSContext.Current;
			string cacheKey = string.Format("TagPosts-S:{0}-R:{1}-S:{2}-T:{3}-PI:{3}-PS:{4}-A:{5}", context.SettingsID, context.User.RoleKey, sectionID, GetTagKeyComponent(tags), pageIndex, pageSize, andTags);

			SearchResultSet result = cacheable ? (SearchResultSet) CSCache.Get(cacheKey) : null;
			if (result == null)
			{
				SearchQuery query = new SearchQuery();
				query.PageSize = pageSize;
				query.PageIndex = pageIndex;
				query.SortBy = SearchSort.DateDescending;
				query.Tags = tags;
				query.SectionsToSearch = new int[] { sectionID };
				query.LogicallyOrTags = !andTags;

				result = CSSearch.Search(query);

				if (cacheable)
				{
					if (pageIndex == 0 && (tags == null || tags.Length < 2))
						CSCache.Insert(cacheKey, result, CSCache.MinuteFactor * 3, CacheItemPriority.Normal);
					else
						CSCache.Insert(cacheKey, result, CSCache.SecondFactorCalculate(10), CacheItemPriority.Low);
				}
			}

			return result;
		}

		public static SearchResultSet GetPostsMatchingTags(ApplicationType applicationType, string[] tags, int pageIndex, int pageSize, bool andTags, bool cacheable)
		{
			CSContext context = CSContext.Current;
			string cacheKey = string.Format("TagPosts-S:{0}-R:{1}-A:{2}-T:{3}-PI:{3}-PS:{4}-A:{5}", context.SettingsID, context.User.RoleKey, applicationType, GetTagKeyComponent(tags), pageIndex, pageSize, andTags);

			SearchResultSet result = cacheable ? (SearchResultSet) CSCache.Get(cacheKey) : null;
			if (result == null)
			{
				SearchQuery query = new SearchQuery();
				query.PageSize = pageSize;
				query.PageIndex = pageIndex;
				query.SortBy = SearchSort.DateDescending;
				query.Tags = tags;
				query.LogicallyOrTags = !andTags;
				query.ApplicationTypes = new ApplicationType[] { applicationType };

				result = CSSearch.Search(query);

				if (cacheable)
				{				
					if (pageIndex == 0 && (tags == null || tags.Length < 2))
						CSCache.Insert(cacheKey, result, CSCache.MinuteFactor * 10, CacheItemPriority.Normal);
					else
						CSCache.Insert(cacheKey, result, CSCache.SecondFactorCalculate(30), CacheItemPriority.Low);
				}
			}

			return result;
		}

		#endregion

		#region RemoveUnusedTags

		public static void RemoveUnusedTagsFromSection(int sectionID)
		{
			RemoveUnusedTagsFromSections(new int[] { sectionID });
		}

		public static void RemoveUnusedTagsFromSections(int[] sectionIDs)
		{
			foreach (PostCategory pc in PostCategories.GetEmptyCategoriesInSections(sectionIDs))
				PostCategories.DeleteCategory(pc.CategoryID, pc.SectionID);
		}

		public static void RemoveUnusedTagsFromGroup(int groupID)
		{
			RemoveUnusedTagsFromGroups(new int[]  { groupID });
		}

		public static void RemoveUnusedTagsFromGroups(int[] groupIDs)
		{
			foreach (PostCategory pc in PostCategories.GetEmptyCategoriesInGroups(groupIDs))
				PostCategories.DeleteCategory(pc.CategoryID, pc.SectionID);
		}

		public static void RemoveUnusedTags(ApplicationType applicationType)
		{
			foreach (PostCategory pc in PostCategories.GetEmptyCategories(applicationType))
				PostCategories.DeleteCategory(pc.CategoryID, pc.SectionID);
		}

		public static void RemoveUnusedTags()
		{
			foreach (PostCategory pc in PostCategories.GetEmptyCategories())
				PostCategories.DeleteCategory(pc.CategoryID, pc.SectionID);
		}

		#endregion

		private static string GetTagKeyComponent(string[] tags)
		{
			ArrayList list = new ArrayList(tags);
			list.Sort();
			
			StringBuilder sb = new StringBuilder();
			foreach (string tag in list)
			{
				if (sb.Length > 0)
					sb.Append("/");

				sb.Append(Globals.UrlEncode(tag));
			}

			return sb.ToString();
		}

		public static void ExpireTagsCache()
		{
			CSCache.RemoveByPattern("Tags\\-.*");
		}

		public static void ExpirePostsMatchingTagsCache()
		{
			CSCache.RemoveByPattern("TagPosts\\-.*");
		}
	}
}
